#!/usr/bin/python
import sys
import os.path
import getopt
import string
from shutil import move
from time import sleep
from pprint import pprint
from ConfigParser import SafeConfigParser
import zlib

from streamcrypt import streamcrypt
from torrent import torrentfile
from bmanager import bmanager

def usage():
    print """ Usage:

btsclient.py [options]

  --help           : display this text
  --rootdir=<dir>  : use this root directory   (default: current directory)
  --dropdirs=<dir> : publish files appearing in this dirctory (default: rootdir/dropdirs)
  --config=<file>  : run from this config file (default: rootdir/setup/btsclient.conf)

"""
    
    sys.exit()

def main():
    try:
        opts, args = getopt.getopt(sys.argv[1:], "hr:d:c:v", ["help", "rootdir=", "dropdirs=", "config="])
    except getopt.GetoptError:
        # print help information and exit:
        usage()

    rootdir = os.getcwd()
    dropdirs = None
    config = None
    verbose = False
    for o, a in opts:
        if o == "-v":
            verbose = True
        if o in ("-h", "--help"):
            usage()
        if o in ("-r", "--rootdir"):
            rootdir = a
        if o in ("-d", "--dropdirs"):
            dropdirs = a
        if o in ("-c", "--config"):
            config = a    

    if config == None:
        config = os.path.join(rootdir, 'setup', 'btsclient.conf')

    if dropdirs == None:
        dropdirs = os.path.join(rootdir, 'dropdirs')
        
    cfg = SafeConfigParser();
    cfg.read(os.path.expanduser(config))
    
    bm = bmanager(cfg.get('server', 'url'),
                  cfg.get('server', 'login'),
                  cfg.get('server', 'password'), 
                  verbose)
    
    print "users:"
    pprint(bm.get_users())
    print "my filesets:"
    pprint(bm.get_my_filesets())
    print "my subscriptions:"
    pprint(bm.get_my_subscribed_filesets())
    
    filesets = {}
    for fs in cfg.get('client', 'filesets').split(','):
        if not cfg.has_section(fs):
            raise ConfigError('No section for fileset %s in configfile' %fs)
        filesets[fs] = {}
        filesets[fs]['compress'] = true_value(cfg_or_default(cfg, fs, 'compress', default=1))
        filesets[fs]['compression_level'] = int(cfg_or_default(cfg, fs, 'compression-level', default=6))
        
        filesets[fs]['encrypt'] =  true_value(cfg_or_default(cfg,fs,'encrypt',0))
        filesets[fs]['password'] = cfg_or_default(cfg,fs,'password','')

    trackerurl = cfg.get('server','url')

    dropdir_mainloop(rootdir, dropdirs, filesets, bm, trackerurl)
            
    bm.logout()

def dropdir_mainloop(rootdir, dropdirs, filesets, bm, trackerurl):
    published_files = {}

    tmpdir     = os.path.join(rootdir, 'tmp')
    publishdir = os.path.join(rootdir, 'publish')
    
    while 1:
        for fs in filesets.keys():
            d = os.path.join(dropdirs, fs)
            if not os.path.exists(d):
                os.mkdir(d)
            if not os.path.isdir(d):
                raise NotADirectory(d)
            files = os.listdir(d)
            # new files
            for f in files:
                if f not in published_files.keys():
                    
                    suffix = ''
                    compress  = None
                    if filesets[fs]['compress']:
                        compress = filesets[fs]['compression_level']
                        suffix += '.gz'

                    encrypt = None
                    if filesets[fs]['encrypt']:
                        encrypt = filesets[fs]['password']
                        suffix += '.aes'

                    relfilename        = os.path.join(fs,f)
                    absfilename        = os.path.join(d,f)
                    tmpfilename        = os.path.join(tmpdir, fs+'__'+f+suffix)
                    pubfilename        = os.path.join(publishdir, fs+'__'+f+suffix)
                    torrentfilename    = pubfilename+'.torrent'
                    tmptorrentfilename = tmpfilename+'.torrent'

                    published_files[f] = {'published_name' : relfilename}
                    print 'New torrent: %s' % torrentfilename

                    if not os.path.exists(pubfilename):
                        # copy the file over into the temporary directory
                        # while compressing and encrypting it
                        copy_compress_encrypt(src = absfilename,
                                              dst = tmpfilename,
                                              compress = compress,
                                              encrypt = encrypt
                                              )
                        # when done, move the ready to publish file to the
                        # publish directory from where it will be sent to the remote nodes
                        move(tmpfilename, pubfilename)

                    tf = torrentfile()
                    if not os.path.exists(torrentfilename):
                        print "making torrent for file ", f
                        tf.make(pubfilename, trackerurl)
                        tf.write(tmptorrentfilename)

                        move(tmptorrentfilename, torrentfilename)
                    else:
                        tf.read(torrentfilename)

                    published_files[f]['id'] = bm.publish_file(fs, tf)
                            
            sleep(1)


# copys a file from a to b, while doing so, it optionally compresses
# and encrypts it
#
# compress : None means, don't compress, 0-9 give compression effort
# encrypt  : None means, don't encrypt, anything else means encryption password
def copy_compress_encrypt(src, dst, compress, encrypt):    
    fin = open(src, 'rb')    
    fout = open(dst, 'wb')

    print "src=%s dst=%s compress=%s encrypt=%s" % (src, dst, compress, encrypt)

    compressor = None
    if compress != None:
        compressor = zlib.compressobj(compress)

    cipher = None
    if encrypt != None:
        cipher = streamcrypt(encrypt)
        cipher.encStart()

    # handle the bulk of the data
    block_size = 1024
    txt = fin.read(block_size)
    while(len(txt)):
        if compressor != None:
            txt = compressor.compress(txt)

        if cipher != None:
            txt = cipher.encNext(txt)
        
        fout.write(txt)
        txt = fin.read(block_size)

    # handle the remaining bits of compressed data
    if compressor != None:
        txt = compressor.flush()
        if cipher != None:
            txt = cipher.encNext(txt)
        fout.write(txt)

    # handle the remaining bits of encrypted data
    if cipher != None:
        fout.write(cipher.encEnd())

    fin.close()
    fout.close()

# make a 0,1, yes,no, triue,false into a 0 or 1
def true_value(str):
    if string.lower(str) == 'no' or str == 0 or string.lower(str) == 'false':
        return 0
    return 1

def cfg_or_default(cfg, section, option, default):
    if cfg.has_option(section,option):
        return cfg.get(section,option)
    else:
        return default

        
# run
if __name__ == "__main__":
    main()

        
